package com.yuxl.admin.manager.auth.security.config;

import com.yuxl.admin.common.security.annotation.NoAuth;
import com.yuxl.admin.common.security.config.JwtProperties;
import com.yuxl.admin.common.security.config.SecurityProperties;
import com.yuxl.admin.common.security.service.CustomUserDetailsService;
import com.yuxl.admin.common.security.service.JwtService;
import com.yuxl.admin.common.security.service.RedisTokenService;
import com.yuxl.admin.manager.auth.security.filter.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtService jwtService;
    private final JwtProperties jwtProperties;
    private final RedisTokenService redisTokenService;
    private final RequestMappingHandlerMapping handlerMapping;
    private final SecurityProperties securityProperties;

    /**
     * 配置安全过滤器链
     * @param http ServerHttpSecurity对象
     * @return 配置好的SecurityWebFilterChain
     */
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        List<String> permitAllPaths = new ArrayList<>(getPermitAllPaths());
        permitAllPaths.add("/auth/login");
        permitAllPaths.add("/auth/logout");
        permitAllPaths.add("/ws/**");
        if (securityProperties.getPermitAllPaths() != null) {
            permitAllPaths.addAll(securityProperties.getPermitAllPaths());
        }

        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)  // 禁用CSRF保护
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers(permitAllPaths.toArray(new String[0])).permitAll()  // 允许这些路径无需认证
                        .anyExchange().authenticated()  // 其他所有路径需要认证
                )
                // 添加JWT认证过滤器
                .addFilterAt(new JwtAuthenticationFilter(jwtService, redisTokenService,jwtProperties), SecurityWebFiltersOrder.AUTHENTICATION)
                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)  // 禁用HTTP Basic认证
                .formLogin(ServerHttpSecurity.FormLoginSpec::disable)  // 禁用表单登录
                .exceptionHandling(exceptionHandling -> exceptionHandling
                        .authenticationEntryPoint((exchange, ex) -> {
                            // 设置未授权访问的响应
                            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                            exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
                            String responseBody = """
                                    {
                                    "code":401,
                                    "msg":"未授权访问"
                                    }
                                    """;

                            return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
                                    .bufferFactory().wrap(responseBody.getBytes(StandardCharsets.UTF_8))));
                        })
                        .accessDeniedHandler((exchange, denied) -> {
                            // 设置访问被拒绝的响应
                            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                            exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
                            String responseBody = "{\"错误\":\"访问被拒绝\"}";
                            return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
                                    .bufferFactory().wrap(responseBody.getBytes(StandardCharsets.UTF_8))));
                        })
                )
                .build();  // 构建SecurityWebFilterChain
    }

    /**
     * 获取所有标记为NoAuth的路径
     * @return 无需认证的路径列表
     */
    private List<String> getPermitAllPaths() {
        List<String> permitAllPaths = new ArrayList<>();
        handlerMapping.getHandlerMethods().forEach((key, value) -> {
            if (AnnotationUtils.findAnnotation(value.getBeanType(), NoAuth.class) != null ||
                    AnnotationUtils.findAnnotation(value.getMethod(), NoAuth.class) != null) {
                key.getPatternsCondition().getPatterns().forEach(pattern -> 
                    permitAllPaths.add(pattern.getPatternString())
                );
            }
        });
        return permitAllPaths;
    }
}
